﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace proj01
{
    public class BatField
    {

        public class Bat
        {
            public int model;
            public Vector3 position;
            public Matrix Finalposition;
            public float size;

        }

        private Game1 game;
        private Model model;

        bool up = false;
        int inwingL;
        int inwingR;
        float inwingAngle = 1.35f;
        int outwingL;
        int outwingR;

        private float angle = 0;
        private float rotationSpeed = 0.1f;

        Quaternion orientation = Quaternion.Identity;


        private LinkedList<Bat> bats = new LinkedList<Bat>();

        public LinkedList<Bat> Bats { get { return bats; } }

        public float InwingAngle { get { return inwingAngle; } set { inwingAngle = value; } }


        private Random random = new Random();

        public BatField(Game1 game)
        {
            this.game = game;
        }

        public void Initialize()
        {
            orientation = Quaternion.Identity;
        }

        /// <summary>
        /// This function is called to load content into this component
        /// of our game.
        /// </summary>
        /// <param name="content">The content manager to load from.</param>
        public void LoadContent(ContentManager content)
        {
            model = content.Load<Model>("Bat-rigid");
            inwingL = model.Bones.IndexOf(model.Bones["LeftWing1"]);
            inwingR = model.Bones.IndexOf(model.Bones["RightWing1"]);
            outwingL = model.Bones.IndexOf(model.Bones["LeftWing2"]);
            outwingR = model.Bones.IndexOf(model.Bones["RightWing2"]);

        }

        /// <summary>
        /// This function is called to update this component of our game
        /// to the current game time.
        /// </summary>
        /// <param name="gameTime"></param>
        public void Update(GameTime gameTime)
        {
            float wingDeployTime = 2.0f; 
            float delta = (float)gameTime.ElapsedGameTime.TotalSeconds;
            foreach (Bat bat in bats)
            {
                if (inwingAngle <= 1.35f && up == false)
                {
                    inwingAngle -= (float)(0.2 * delta / wingDeployTime);
                    if (inwingAngle <= -1.35f)
                    {
                        inwingAngle = -1.35f;
                        up = true;
                    }
                }
                else if (inwingAngle >= -1.35f)
                {
                    inwingAngle += (float)(0.2 * delta / wingDeployTime);
                    if (inwingAngle >= 1.35f)
                    {
                        inwingAngle = 1.35f;
                        up = false;
                    }
                }



                Matrix transform = Matrix.CreateFromQuaternion(orientation);
                angle += rotationSpeed * delta;

            }
        }

        public void BatCreate()
        {
            while (bats.Count < 12)
            {
                Bat bat = new Bat();
                bat.model = random.Next(4);
                bat.position = RandomVector(-2000, 2000);
                bat.size = 50;
                if (bat.position.Length() < 1000)
                    continue;

                bats.AddLast(bat);
            }
        }

        /// <summary>
        /// This function is called to draw this game component.
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="gameTime"></param>
        public void Draw(GraphicsDeviceManager graphics, GameTime gameTime)
        {
            float drawangle = angle;
            //DrawModel(graphics, models[0], Matrix.CreateTranslation(0, 300, 0));
            if (bats.First == null)
                game.endScreen = true;
            foreach (Bat bat in bats)
            {
                bat.Finalposition = Matrix.CreateTranslation(bat.position)
                    * Matrix.CreateTranslation(3000, 0, 0)
                    * Matrix.CreateRotationY((float)Math.PI/4)
                    * Matrix.CreateFromAxisAngle(new Vector3(0, 1, 0), -angle)
                    * Matrix.CreateTranslation(-3000, 0, 0);

                DrawModel(graphics, model, Matrix.CreateScale(bat.size) * Matrix.CreateFromAxisAngle(new Vector3(0, 0, 1), 0.78f)
                    * bat.Finalposition); 

            }
        }


        private void DrawModel(GraphicsDeviceManager graphics, Model model, Matrix world)
        {
            Matrix[] transforms = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(transforms);

           
            int count = model.Bones.Count;
            for (int i = 0; i < count; i++)
            {
                if (i == inwingL)
                    transforms[i] = Matrix.CreateRotationY(-inwingAngle);
                else if (i == inwingR)
                    transforms[i] = Matrix.CreateRotationY(inwingAngle);
                else
                    transforms[i] = Matrix.Identity;
                ModelBone bone = model.Bones[i];
                if (bone.Parent == null)
                {
                    transforms[i] *= bone.Transform;
                }
                else
                {
                    transforms[i] *= 0.1f * bone.Transform * transforms[bone.Parent.Index];
                }
            }
         

            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();
                    effect.World = transforms[mesh.ParentBone.Index] * world;
                    effect.View = game.Camera.View;
                    effect.Projection = game.Camera.Projection;
                }
                mesh.Draw();
            }
        }

        private Vector3 RandomVector(float min, float max)
        {
            return new Vector3((float)Math.Abs(min + (random.NextDouble() * (max - min))),
                (float)Math.Abs(min + (random.NextDouble() * (max - min))) + 500,
                (float)(min + (random.NextDouble() * (max - min))));
        }


        public bool TestLaserForCollision(Vector3 position)
        {
            for (LinkedListNode<Bat> batNode = bats.First; batNode != null; )
            {
                LinkedListNode<Bat> nextNode = batNode.Next;
                Bat bat = batNode.Value;

                // Obtain a bounding sphere for the asteroid.  I can get away
                // with this here because I know the model has exactly one mesh
                // and exactly one bone.
                BoundingSphere bs = model.Meshes[0].BoundingSphere;
                bs = bs.Transform(model.Bones[0].Transform);
                // Move this to world coordinates.  Note how easy it is to 
                // transform a bounding sphere
                bs.Radius *= bat.size;
                bs.Center += bat.Finalposition.Translation;

                if ((position - bs.Center).LengthSquared() < bs.Radius * bs.Radius)
                {
                    // We have a hit.  We'll delete this asteroid and return true
                    bats.Remove(batNode);
                    game.Score += 10;
                    game.SoundBank.PlayCue("explosion2");
                    return true;
                }
                batNode = nextNode;
            }
            return false;
        }



    }
}
